package com.ybq.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

/**
 * 1. 首先，我们重写 #configure(AuthenticationManagerBuilder auth) 方法，实现 AuthenticationManager 认证管理器。代码如下：
 * 2. 然后，我们重写 #configure(HttpSecurity http) 方法，主要配置 URL 的权限控制。
 * 3. 跑起来测试下访问各类 url 的效果
 * 》》》》》》
 * 4. 使用注解进行权限控制
 *
 *
 * @author ly
 * @email 664162337@qq.com
 * @date 2020/9/28 17:40
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    /**
     * 1. 实现 AuthenticationManager 认证管理器
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                // 1. 使用内存中的 InMemoryUserDetailsManager
                .inMemoryAuthentication()
                // 2. 不使用 PasswordEncoder 密码编码器，否则用户的密码需要使用 spring5 提供的加密方式加密密码
                .passwordEncoder(NoOpPasswordEncoder.getInstance())
                // 3. 配置用户 admin
                .withUser("admin").password("admin").roles("ADMIN")
                // 配置用户 user1
                .and().withUser("user1").password("user1").roles("USER")
        ;
        /*
        1. 调用 AuthenticationManagerBuilder#inMemoryAuthentication() 方法，
            使用内存级别的 InMemoryUserDetailsManager Bean 对象，提供认证的用户信息
            Spring 内置了两种 UserDetailsManager 实现：
                a). InMemoryUserDetailsManager，和我们在 SpringSecurityApp 1~4 步骤是一样的。
                b). JdbcUserDetailsManager ，基于 JDBC 的 JdbcUserDetailsManager 。
            实际项目中，我们更多采用调用 AuthenticationManagerBuilder#userDetailsService(userDetailsService) 方法，使用自定义实现的 UserDetailsService 实现类，更加灵活且自由的实现认证的用户信息的读取
        2. 调用 AbstractDaoAuthenticationConfigurer#passwordEncoder(passwordEncoder) 方法，设置 PasswordEncoder 密码编码器
            生产环境下，推荐使用 BCryptPasswordEncoder 。更多参考：http://www.iocoder.cn/Spring-Security/laoxu/PasswordEncoder/?self
        3. 配置 2 个用户（注意这里配置了用户，那么配置文件中配置的用户信息就失效了。）
         */
    }
    
    /**
     * 2. 主要配置 http url 的权限控制
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 1. 配置请求地址的权限
                .authorizeRequests()
                .antMatchers("/").permitAll() // 所有人都可以访问 /
                .antMatchers("/user1").hasRole("USER") // 访问/user1 的链接，需要 user1 的权限
                .antMatchers("/user2").access("hasRole('ADMIN')") // 访问 /user2，需要 admin 角色
                .anyRequest().authenticated() // 任何请求，访问的用户都要经过认证（认证即登录，授权是有什么权限）
                .and().formLogin()// 如果未通过认证，跳到登录页，默认 /login
                // .loginPage("/login") // 可以自定义登录页地址
                .permitAll() // 所有用户都可以访问 login
                .and()
                .logout()
                // .logoutUrl("/logout")
                .permitAll() // 所有用户都可以访问 logout
        ;
    }
}
